package demo01

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

// 1. sync.WaitGroup
/*
在前面很多goroutine的示例中，我们都是通过time.Sleep()方法让主goroutine等待一段时间以便子gortoutine能够执行完打印结果，
显然这不是一个很好的办法，因为我们不知道所有的子gortoutine要多久才能执行完，不能确切的知道需要等待多久。那怎么处理呢？
*/

// ChannelDemo 1.1 方式一：channel
/*
我们在每个goroutine中，向管道里发送一条数据，这样我们在程序最后，通过for循环将管道里的数据全部取出，直到数据全部取出完毕才能继续后面的逻辑，这样就可以实现等待各个goroutine执行完
但是，这样使用channel显得并不优雅，其次，我们得知道具体循环的次数，来创建管道的大小，假设次数非常的多，则需要申请同样数量大小的管道出来，对内存也是不小的开销
*/
func ChannelDemo() {
	ch := make(chan struct{}, 10)
	for i := 0; i < 10; i++ {
		go func(i int) {
			fmt.Printf("num:%d\n", i)
			ch <- struct{}{}
		}(i)
	}

	for i := 0; i < 10; i++ {
		<-ch
	}

	fmt.Println("end")
}

func myGoroutine(i int) {
	defer wg.Done()
	fmt.Printf("myGoroutine-%d!\n", i)
}

// WaitGroupDemo 1.2 方式二：sync.WaitGroup
/*
这里我们可以用sync包下的WaitGroup来实现，Go语言中可以使用sync.WaitGroup来实现并发任务的同步以及协程任务等待。
sync.WaitGroup是一个对象，里面维护者一个计数器，并且通过三个方法来配合使用

(wg * WaitGroup) Add(delta int) 计数器加delta
(wg *WaitGroup) Done() 计数器减1
(wg *WaitGroup) Wait() 会阻塞代码的运行，直至计数器减为0

程序首先把wg的 计数设置为10， 每个for循环运行完毕都把计数器减1，main函数中执行到wg.Wait()会一直阻塞，直到wg的计数器为零。
最后打印了10个myGoroutine!，是所有子goroutine任务结束后主goroutine才退出。
注意： sync.WaitGroup对象的计数器不能为负数，否则会panic，在使用的过程中，我们需要保证add()的参数值，以及执行完Done()之后计数器大于等于零
*/
func WaitGroupDemo() {
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go myGoroutine(i)
	}
	wg.Wait()
	fmt.Println("wg done")
}
